package com.ht.api.init;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ht.api.annotate.Authorize;
import com.ht.api.annotate.OpenApi;

import lombok.extern.slf4j.Slf4j;

/**
 * 初始化加载所有接口到缓存
 * @author asq
 * @createTime 2024年12月21日 19:55:44
 */
@Component
@Slf4j
public class ApiInitializer implements ApplicationRunner {
	/** 完全公开（不用登陆即可访问）的Api接口（有OpenApi注解的接口） */
	public static List<String> openApis = new ArrayList<>();
	/** 需要鉴权的接口 */
	public static Map<String, String> authApis = new HashMap<>();
	
	@Autowired
	private ApplicationContext applicationContext;

	public ApiInitializer() {
	}

	@Override
	public void run(ApplicationArguments var1) throws Exception {
		//将所有的接口对应的url缓存下来
		initApis();
	}

	/**
	 * 扫描所有controller，缓存所有开放给APP的api接口，供权限拦截器以及swagger文档使用
	 */
	private void initApis() {
		Map<String, Object> controllers = applicationContext.getBeansWithAnnotation(Controller.class);
		for (Map.Entry<String, Object> entry : controllers.entrySet()) {
			findAndAddClassesInPackageByFile(entry.getValue().getClass());
		}
	}

	/** 
	 * 传入一个controller,将其下所有的接口缓存下来（如果该接口上有@App注解） 
	 * @param cls controller对应的class
	 */
	private void findAndAddClassesInPackageByFile(Class<?> cls) {
		//获取类上的@RequestMapping注解
		String urlParent = null;
		RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);
		if (requestMapping != null && requestMapping.value().length > 0) {
			urlParent = requestMapping.value()[0];
		} else {
			urlParent = "";
		}

		//遍历方法
		Method[] marr = cls.getMethods();
		for (Method method : marr) {
			//检查是否有@OpenApi注解
			OpenApi openApi = method.getAnnotation(OpenApi.class);
			if (openApi != null) {
				String url = getApiUrl(urlParent, method);

				//将有@OpenApi注解的接口对应的url保存起来，以便开放访问，不走拦截器
				log.info("识别openApi接口：{}", url);
				openApis.add(url);
			}
			
			//检查是否有@Authorize注解
			Authorize authorize = method.getAnnotation(Authorize.class);
			if (authorize != null) {
				String url = getApiUrl(urlParent, method);
				
				//将有@Authorize注解的接口对应的url保存起来，以便拦截权限
				log.info("识别authorize接口：{}", url);
				authApis.put(url, authorize.value());
			}
		}
	}

	/**
	 * 获取Api的完整映射路径
	 * @param urlParent 类的映射路径
	 * @param method controller的方法
	 * @return
	 */
	private String getApiUrl(String urlParent, Method method) {
		//获取方法上配置的映射路径
		String urlMethod = getAnnotationUrl(method);

		//组装成完整的url路径，并确保斜杠不多不少
		StringBuilder sb = new StringBuilder();
		if (urlParent.endsWith("/")) {
			sb.append(urlParent.substring(0, urlParent.length() - 1));
		} else {
			sb.append(urlParent);
		}
		sb.append("/");
		if (urlMethod.startsWith("/")) {
			sb.append(urlMethod.substring(1, urlMethod.length()));
		} else {
			sb.append(urlMethod);
		}
		String url = sb.toString();
		return url;
	}

	/**
	 * 获取方法上的接口路径映射名称
	 * @param method
	 * @return
	 */
	private String getAnnotationUrl(Method method) {
		//获取@PostMapping注解
		GetMapping gm = method.getAnnotation(GetMapping.class);
		if (gm != null && gm.value().length > 0) { return gm.value()[0]; }

		//获取@PostMapping注解
		PostMapping pm = method.getAnnotation(PostMapping.class);
		if (pm != null && pm.value().length > 0) { return pm.value()[0]; }

		//获取@PostMapping注解
		RequestMapping rm = method.getAnnotation(RequestMapping.class);
		if (rm != null && rm.value().length > 0) { return rm.value()[0]; }

		return "";
	}

}
